
mc_nether.find_surface_target_attempts = 2

-- Can be used when implementing custom find_surface_anchorPos() functions
-- portal_name is optional, providing it allows existing portals on the surface to be reused, and
-- a potentially smaller volume to be checked by volume_is_natural_and_unprotected().
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
function nether.find_surface_target_y(target_x, target_z, portal_name, player_name)

	assert(target_x ~= nil and target_z ~= nil, "Arguments `target_x` and `target_z` cannot be nil when calling find_surface_target_y()")

	-- default to starting the search at -16 (probably underground) if we don't know the
	-- surface, like paramat's original code from before get_spawn_level() was available:
	-- https://github.com/minetest-mods/nether/issues/5#issuecomment-506983676
	local start_y = -16

	-- try to spawn on surface first
	if minetest.get_spawn_level ~= nil then -- older versions of Minetest don't have this
		local surface_level = minetest.get_spawn_level(target_x, target_z)
		if surface_level ~= nil then -- test this since get_spawn_level() can return nil over water or steep/high terrain

			-- get_spawn_level() tends to err on the side of caution and spawns the player a
			-- block higher than the ground level. The implementation is mapgen specific
			-- and -2 seems to be the right correction for v6, v5, carpathian, valleys, and flat,
			-- but v7 only needs -1.
			-- Perhaps this was not always the case, and -2 may be too much in older versions
			-- of minetest, but half-buried portals are perferable to floating ones, and they
			-- will clear a suitable hole around themselves.
			if minetest.get_mapgen_setting("mg_name") == "v7" then
				surface_level = surface_level - 1
			else
				surface_level = surface_level - 2
			end
			start_y = surface_level
		end
	end

	local minp_schem, maxp_schem = nether.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, portal_name)
	local minp = {x = minp_schem.x, y = 0, z = minp_schem.z}
	local maxp = {x = maxp_schem.x, y = 0, z = maxp_schem.z}

	-- Starting searchstep at -16 and making it larger by 2 after each step gives a 20-step search range down to -646:
	-- 0, -16, -34, -54, -76, -100, -126, -154, -184, -216, -250, -286, -324, -364, -406, -450, -496, -544, -594, -646
	local searchstep = -16;

	local attempts = -1 * searchstep * mc_nether.find_surface_target_attempts +1

	local y = start_y
	while y > start_y - attempts do
		-- Check volume for non-natural nodes
		minp.y = minp_schem.y + y
		maxp.y = maxp_schem.y + y
		if nether.volume_is_natural_and_unprotected(minp, maxp, player_name) then
			return y
		elseif portal_name ~= nil and nether.registered_portals[portal_name] ~= nil then
			-- players have built here - don't grief.
			-- but reigniting existing portals in portal rooms is fine - desirable even.
			local anchorPos, orientation, is_ignited = is_within_portal_frame(nether.registered_portals[portal_name], {x = target_x, y = y, z = target_z})
			if anchorPos ~= nil then
				debugf("volume_is_natural_and_unprotected check failed, but a portal frame is here %s, so this is still a good target y level", anchorPos)
				return y
			end
		end
		y = y + searchstep
		searchstep = searchstep - 2
	end

	return nil -- Portal ignition failure. Possibly due to a large protected area.
end